/*
 * Copyright 2007 - The JDPF Project Team (http://www.jdpf.org) 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package org.jdpf.plugins.measureseries.aggregator.measureseries.measureseriesaggregator;

import java.util.List;

import javax.measure.Measure;
import javax.measure.unit.Unit;

import org.jdpf.core.kernel.elements.Token;
import org.jdpf.core.plugins.blocks.AbstractAggregatorPlugin;
import org.jdpf.core.plugins.blocks.AbstractFilterPlugin;
import org.jdpf.core.plugins.exceptions.PluginException;
import org.jdpf.core.plugins.exceptions.PluginExceptionsEnum;
import org.jdpf.plugins.datatypes.basic.MeasureAdapter;
import org.jdpf.plugins.datatypes.basic.MeasureSeries;
import org.jscience.physics.amount.Amount;

/**
 * The measure Aggregator combines two measure series according to the function selected.
 * The default function is the mean. The values that are out of range 
 * can be rejected or clipped (changed to the closest allowed value).
 * 
 * @author Cristiana Larizza
 * @version 1.0
 */

public class MeasuresAggregator extends AbstractAggregatorPlugin {
	
	/*
	 * Parameters list. We prefer to define the plugin parameters with an 
	 * underscore. For every plugin parameter it is necessary to implement 
	 * a 'set' method that is accepting a String variable. The framework,
	 * through reflection, will set the parameters at runtime. 
	 */
	
	private Boolean _enableClipping = true;	// Optional
	private String _function = "mean";		// Optional
	private Unit _unitOfMeasure = Unit.ONE;	// Optional

	/**
	 * The clipping parameter establish if the out of range value has to 
	 * be rejected or clipped to the closest bound.
	 * @param enableClipping
	 */
	public void setEnableClipping(Boolean enableClipping) {
		this._enableClipping = enableClipping;
	}

	/**
	 * @param function	The lower bound in double precision.
	 */
	public void setFunction(String function) {
		this._function = function;
	}
	
	public void setUnitOfMeasure(String unitOfMeasure) {
		_unitOfMeasure = Unit.valueOf(unitOfMeasure.subSequence(0, unitOfMeasure.length()));
	}
	
	/*
	 * Standard plugin constructor: every plugin has to implement the 
	 * constructor in this very same format.
	 */
	public MeasuresAggregator(String transitionId, String transitionName){
		super(transitionId, transitionName);
	}
	
	public int getFunction(){
		int ret = 0;
		this.log("in getFunction()");
		if(_function.equals("mean"))
			ret = 1;
		else 		if(_function.equals("min"))
			ret = 2;
		else 		if(_function.equals("max"))
			ret = 3;
		else 		if(_function.equals("sum"))
			ret = 4;
		ret = 1;
		this.log("function = " + ret);
		return ret;
	}
	
	/*
	 * This is the actual algorithm implementation. All the exceptions 
	 * generated by this code are catched in the upper level. This 
	 * method can access the cvariable 'percentage' that is used by the
	 * monitors to show the elaboration progress. 
	 */
	public Token process(List<Token> list) throws PluginException {
		this.logMainPoint(this, this.getPluginId(), this.getId(), "values Aggregation started.\n");
//		this.log("dimensione " +list.size()+"\tclasse " +list.get(0).getClass() );
//		this.log("I token "+(Token)(list.get(0))+ " tipo " + list.get(0).getValueList().get(0).getType());
//		this.log("II token "+(Token)(list.get(1))+ " classe " + list.get(1).getValueList().get(0).getClass());

		List inList1 = list.get(0).getValueList();
 		MeasureSeries ms1 = (MeasureSeries) inList1.get(0);
		List inList2 = list.get(1).getValueList();
 		MeasureSeries ms2 = (MeasureSeries) inList2.get(0);
		
		return process(ms1, ms2);
//		return (Token)list.get(0);
	}
		
	/*
	 * This is the actual algorithm implementation. All the exceptions 
	 * generated by this code are catched in the upper level. This 
	 * method can access the cvariable 'percentage' that is used by the
	 * monitors to show the elaboration progress. 
	 */
	public Token process(MeasureSeries ms1, MeasureSeries ms2) throws PluginException {
		MeasureSeries tmp;
		if(ms1.getSize()>ms2.getSize()){
			tmp = ms1;
			ms1 = ms2;
			ms2 = tmp;
		}
		MeasureAdapter value1, value2;
		MeasureSeries msout = new MeasureSeries();
		this.log("prima del ciclo");
		
		for (int i=0; i<ms1.getSize(); i++){
			if(!start) break; 									// Allows the task to block the elaboration
//			percentage = ((double)(i+1) / ms1.getSize()) * 100; 	// Elaboration progress
			value1 = ((MeasureAdapter) ms1.getElement(i));	
			value2 = ((MeasureAdapter) ms2.getElement(i));	
//			this.log("value1 "+ value1.getStringValue());
//			this.log("value2 "+ value2.getStringValue());
			double v1 = value1.getMeasure().doubleValue(value1.getMeasure().getUnit());
			double v2 = value2.getMeasure().doubleValue(value2.getMeasure().getUnit());
			this.log("v1 "+ v1);
			this.log("v2 "+ v2);
			double value = 0;
			switch(getFunction()){
			case 1:
				value = (v1+v2)/2;
				break;
			case 2:
				value = (v1<v2)?v1:v2;
				break;
			case 3:
				value = (v1>v2)?v1:v2;
				break;
			case 4:
				value = v1+v2;
				break;
			}
			this.log(this, "generating " + value);
			msout.addElement(new MeasureAdapter(Measure.valueOf(value, Unit.ONE)));
//	    	this.logMainPoint(this, this.getPluginId(), "end of processing");

//			if (getAmount(inputValue).isLargerThan(getAmount(_lowerBound)) &&
//					getAmount(inputValue).isLessThan(getAmount(_upperBound))) {
//				this.log(this, "writing " + inputValue.getMeasure().doubleValue(inputValue.getMeasure().getUnit()));
//				msout.addElement(inputValue);
//			}
//			percentage = Math.min(100, ((double)processedBytes / fileLength) * 100);
			
	}
		
		
   	return setOutputToken(msout); // Defines and returns the output token
	}
	
	private Amount getAmount(MeasureAdapter ma) {
		return Amount.valueOf(ma.getMeasure().doubleValue(_unitOfMeasure), _unitOfMeasure);
	}
}
